﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DotNetExtensions.AspNet.IOC
{
    public static class ContainerBuilderExtensions
    {
        public static void RegistTypes(this ContainerBuilder builder, Action<BuilderOption> option)
        {
            BuilderOption builderOption = new BuilderOption();
            option?.Invoke(builderOption);

            if (!builderOption.Assemblies.Any())
            {
                builderOption.Assemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies());
            }

            List<Type> types = builderOption.Assemblies.SelectMany(a => a.GetExportedTypes()).Distinct().ToList();

            // interface | if inherit IAutoInject or use AutoInjectAttribute
            List<Type> autoInjectFromInheritTypes = types.Where(t => t.IsInterface && t.GetInterfaces().Contains(typeof(IAutoInject))).Distinct().ToList();
            List<Type> autoInjectFormAttrTypes = types.Where(t => t.IsInterface && t.GetCustomAttribute<AutoInjectAttribute>() != null).Distinct().ToList();

            if (autoInjectFromInheritTypes.Any() || autoInjectFormAttrTypes.Any())
            {
                List<Type> autoInjectTypes = autoInjectFromInheritTypes.Union(autoInjectFormAttrTypes).Distinct().ToList();
                builderOption.Filters.AddRange(autoInjectTypes.Select(type => new InjectTypeFilter { Kind = type.GetCustomAttribute<AutoInjectAttribute>()?.Kind ?? InjectKind.Typed, Type = type }));
            }

            // class | use AutoInjectAttribute
            List<Type> autoInjectClassTypes = types.Where(t => t.IsClass && t.GetCustomAttribute<AutoInjectAttribute>() != null).Distinct().ToList();
            if (autoInjectClassTypes.Any())
            {
                builderOption.Filters.AddRange(autoInjectClassTypes.Select(type => new InjectTypeFilter { Kind = type.GetCustomAttribute<AutoInjectAttribute>().Kind, Type = type }));
            }
            foreach (var item in builderOption.Filters)
            {
                if (item.Type == typeof(IAutoInject)) continue;
                if (item.Type == typeof(IDisposable)) continue;
                if (item.Type == typeof(object)) continue;

                List<Type> subs = null;

                if (item.Type.IsClass)
                {
                    // if class | use self
                    subs = new List<Type> { item.Type };
                }
                else if (item.Type.IsInterface)
                {
                    subs = types.Where(t => t.IsClass && t.GetInterfaces().Contains(item.Type)).ToList();
                }

                foreach (var type in subs)
                {
                    var @inject = type.GetCustomAttribute<InjectAttribute>();
                    if (@inject == null) @inject = new InjectAttribute();
                    builder.RegisterType(type, item.Type, item.Kind, @inject.Scope, @inject.Named);
                }
            }
        }
    }
}
